#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "rfs-defs.h"

int isfirst = 1;
char cwd[MAX_STRING_SIZE];
int root;

int recibir(PACKAGE *package, struct sockaddr_in *sock, int flag);
int enviar(PACKAGE *package, struct sockaddr_in sock, int mode);
int segmentar(char * data, PACKAGE * package, struct sockaddr_in sock, int size);
int ensamblar(char ** data, PACKAGE * package, struct sockaddr_in sock);

int get_line(char dest[], FILE * f) {
    int t = 0;
    char c;

    do {
        c = fgetc(f);
        if (c != '\n') dest[t++] = c;
    } while (c != '\n' && c != EOF);
    dest[t++] = '\0';
    if (t == 1)
        return get_line(dest, f);
    else
        return c == EOF ? -1 : t - 1;
}

int read_conf(void) {
    char *buffer, *prop, *value;
    FILE *conf_file = NULL;

    // abrimos el archivo de configuración
    conf_file = fopen(CONF_FILE, "r");
    if (conf_file == NULL) {
        printf("No se puede leer el archivo de configuración.\n");
        return ERROR;
    }

    getcwd(cwd, MAX_STRING_SIZE);

    buffer = (char *) malloc(MAX_STRING_SIZE);
    prop = (char *) malloc(MAX_STRING_SIZE);
    value = (char *) malloc(MAX_STRING_SIZE);

    printf("Configuración del servidor\n");
    printf("-------------------------------------------\n");
    // leemos el archivo linea a linea
    while (get_line(buffer, conf_file) != EOF) {

        // descarto los comentarios
        if (buffer[0] == COMMENT) {
            continue;
        }
        // 'tokenizo' la linea, por '='
        strcpy(prop, strtok(buffer, "="));
        strcpy(value, strtok(NULL, "="));

        //si es la especificacion del directorio raiz de trabajo
        if (strcasecmp(prop, ROOT) == 0) {
            int i = 0;
            if ((i = chdir(value)) == 0) {
                isfirst = 0;
                getcwd(cwd, MAX_STRING_SIZE);
                root = strlen(cwd);
                printf("Directorio de trabajo: %s\n", cwd); //Muestra la carpeta pública
            }
        } else
            //si es la especificacion del puerto de escucha
            if (strcasecmp(prop, PORT) == 0) {
            port = atoi(value);
            printf("Puerto de escucha: %i\n", port); //Muestra el puerto de escucha
        }
    }
    printf("-------------------------------------------\n");

    free(buffer);
    free(prop);
    free(value);

    fclose(conf_file);

    return OK;
}

int
rls(PACKAGE *package, struct sockaddr_in sock) {
    // definimos el buffer donde cargaremos el contenido del directorio
    // y el arreglo donde guardamos el nombre del directorio de trabajo
    static char buffer[MAX_STRING_SIZE];

    // 'vaciamos' el buffer para evitar devolver datos sucios
    memset(buffer, '\0', MAX_STRING_SIZE);

    // si es la primera vez que se ejecuta la función,
    // seteamos el directorio de trabajo actual
    if (isfirst == 1) {
        isfirst = 0;
        getcwd(cwd, MAX_STRING_SIZE);
    }

    // estructuras mediante las cuales leeremos el contenido del directorio
    DIR *dp1;
    struct dirent *dirp;

    // abrimos el directorio
    dp1 = opendir(cwd);

    // si no se puede abrir el directorio...
    if (!dp1) {
        strcpy(buffer, "No se pudo leer el directorio.\n");
    } else {
        // leemos contenido del directorio y agregamos los nombres
        // de archivos o directorios al buffer, descartando el directorio
        // actual y el 'padre'
        while ((dirp = readdir(dp1)) != NULL) {
            if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
                continue;
            if (strlen(buffer) != 0)
                strcpy(&buffer[strlen(buffer)], "\n");
            strcpy(&buffer[strlen(buffer)], dirp->d_name);
            // si es un directorio, agrego [DIR] a la salida
            if (dirp->d_type == 0x004)
                strcpy(&buffer[strlen(buffer)], " - [DIR]");

        }
        closedir(dp1);
        // si el buffer quedó vacio, no hay archivos ni directorios
        // que mostrar
        if (strlen(buffer) == 0) {
            strcpy(buffer, "Directorio vacio.\n");
        }
        strcat(&buffer[strlen(buffer)], "\n\0");
        return segmentar(buffer, package, sock, strlen(buffer)+1);
    }
    return ERROR;
}

int
rcd(PACKAGE * package, struct sockaddr_in sock) {
    // si es la primera vez que se ejecuta la función,
    // seteamos el directorio de trabajo actual
    if (isfirst == 1) {
        isfirst = 0;
        getcwd(cwd, MAX_STRING_SIZE);
    }

    int result = -1;

    if (package->data[0] == '/' && strcmp(package->data, "/..") != 0) {
        cwd[root] = '\0';
        result = chdir(strcat(cwd, package->data));
    } else
        if (strlen(cwd) != root || strcmp(package->data, "..") != 0)
        result = chdir(package->data);


    static char res[MAX_STRING_SIZE];
    if (result == 0) {
        strcpy(res, "Se cambió correctamente el directorio.\nEl directorio actual es: ");
        getcwd(cwd, MAX_STRING_SIZE);
        strcat(res, &cwd[root]);
        if (strlen(cwd) == root)
            strcat(res, "/");
    } else {
        strcpy(res, "No se pudo cambiar de directorio.\nUd. intentó cambiar a: ");
        strcat(res, package->data);
    }
    strcat(&res[strlen(res)], "\n\0");
    return segmentar(res, package, sock, strlen(res) + 1);
}

int
get(PACKAGE * package, struct sockaddr_in sock) {
    FILE *file;
    file = fopen(package->data, "r");
    if (file == NULL) {
        package->op = ERROR;
        enviar(package, sock, ACK);
        return ERROR;

    } else {
        char
        buffer[DATA_LENGTH],
                *result = malloc(sizeof (char));
        result[0] = '\0';

        package->op = OK;
        if (enviar(package, sock, ACK) < 0)
            return ERROR;
        int i = 0, n;
        do {
            fseek(file, i * DATA_LENGTH, SEEK_SET);
            n = fread(buffer, sizeof (char), DATA_LENGTH, file);

            result = realloc(result, i * DATA_LENGTH + n);
            memmove(&result[i * DATA_LENGTH], buffer, n);
            i++;
        } while (n == DATA_LENGTH);
        fclose(file);
        return segmentar(result, package, sock, (i - 1) * DATA_LENGTH + n);
    }
}

int
put(PACKAGE * package, struct sockaddr_in sock) {
    // puntero al archivo que se crea del lado del cliente
    FILE *file;
    // abro para crear o sobreescribir el archivo...
    file = fopen(package->data, "w");

    if (file == NULL) {
        package->op = ERROR;
        enviar(package, sock, ACK);
        return ERROR;
    } else {
        char *result = malloc(sizeof (char));
        result[0] = '\0';
        int n;

        package->op = OK;
        if (enviar(package, sock, ACK) < 0)
            return ERROR;
        n = ensamblar(&result, package, sock);
        fwrite(result, sizeof (char), n, file);
        fclose(file);
        return n;
    }
}
